package com.yyfax.pay.order.data.service.impl;

import com.fuiou.mpay.encrypt.DESCoderFUIOU;
import com.fuiou.util.MD5;
import com.google.common.collect.Maps;
import com.yyfax.framework.common.Log;
import com.yyfax.framework.common.OpResponse;
import com.yyfax.framework.service.CacheService;
import com.yyfax.framework.service.SequenceService;
import com.yyfax.framework.service.impl.BaseServiceImpl;
import com.yyfax.framework.yyfax.CryptUtil;
import com.yyfax.framework.yyfax.DesensitizationUtil;
import com.yyfax.framework.yyfax.enums.SequenceStrategy;
import com.yyfax.framework.yyfax.enums.YesNoEnum;
import com.yyfax.pay.common.constants.CacheName;
import com.yyfax.pay.common.constants.FuiouProtocolPayConstant;
import com.yyfax.pay.common.enums.*;
import com.yyfax.pay.common.exception.PayException;
import com.yyfax.pay.common.exception.PayExceptionCode;
import com.yyfax.pay.common.model.vo.PayRspCodeVO;
import com.yyfax.pay.common.util.FuiouIdUtils;
import com.yyfax.pay.config.data.service.BankConfigService;
import com.yyfax.pay.config.model.po.BankConfigPO;
import com.yyfax.pay.depository.service.inner.FuiouDepositCommonService;
import com.yyfax.pay.order.data.dao.UserPaymentDao;
import com.yyfax.pay.order.data.dao.protocol.ProtocolBindingOrderDao;
import com.yyfax.pay.order.data.service.ProtocolBindService;
import com.yyfax.pay.order.data.service.ProtocolPayService;
import com.yyfax.pay.order.data.service.UserAuthService;
import com.yyfax.pay.order.data.service.UserPaymentService;
import com.yyfax.pay.order.model.domain.ProtocolBindingOrderDO;
import com.yyfax.pay.order.model.po.UserAuthPO;
import com.yyfax.pay.order.model.po.UserPaymentPO;
import com.yyfax.pay.order.model.to.route.RouteApiDirectContent;
import com.yyfax.pay.order.model.vo.ProtocolBindSendMsgVO;
import com.yyfax.pay.order.model.vo.ProtocolBindVO;
import com.yyfax.pay.order.model.vo.ProtocolCardQueryVO;
import com.yyfax.pay.order.service.AdapterService;
import com.yyfax.pay.third.facade.fuiou.*;
import com.yyfax.pay.third.model.to.fuiou.*;
import com.yyfax.pay.vo.order.*;
import net.sf.ehcache.search.expression.Or;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.UUID;

@Service
public class ProtocolBindServiceImpl extends BaseServiceImpl implements ProtocolBindService {

	@Resource
	private ProtocolBindingOrderDao protocolBindingOrderDao;

	@Resource
	private UserPaymentService userPaymentService;

	@Resource
	private FuiouIdUtils fuiouIdUtils;

	@Resource
	private UserAuthService userAuthService;

	@Resource
	private ProtocolAdaptService protocolAdaptService;

	@Resource
	private SequenceService sequenceService;

	protected Logger logger = LoggerFactory.getLogger(getClass());

	public ProtocolBindServiceImpl(@Qualifier("payCacheService") CacheService cacheService) {
		super(cacheService);
	}

	/**
	 * 协议绑定发送验证码
	 * 
	 * @author wangjwy
	 * @date 2018/6/5 14:13
	 * @since 2.2.0
	 * @param param
	 * @return
	 * @throws Exception
	 */
	public OpResponse protocolBindSendMsg(ProtocolBindSendMsgReq param, String merchantCode) {
		final String OP = "ProtocolBindServiceImpl.protocolBindSendMsg";

		// 校验用户是否做过协议绑定
		UserPaymentPO userPaymentPO = userPaymentService.query(merchantCode, param.getUserId(), PaymentEnum.FUIOU_PROTOCOL_PAY.getValue());
		if (userPaymentPO != null && StringUtils.isNotBlank(userPaymentPO.getProtocolCode()) && YesNoEnum.YES.isValue(userPaymentPO.getState())) {
			// 查到存在协议号,直接返回
			logger.info(Log.op(OP).msg("查到用户存已存在绑定协议号,直接返回").kv("orderId", param.getOrderId()).toString());
			throw PayExceptionCode.ERROR_EXISI_PROTOCOL.exp();
		}

		// 短信次数控制
		boolean add = cacheService.add(CacheName.PROTOCOL_MESSAGE_KEY_PREFIX + param.getUserId(), param, FuiouProtocolPayConstant.CACHETIME);
		if (!add) {
			// 发送验证码间隔3分钟
			logger.warn(Log.op(OP).msg("请求验证码太频繁,请稍后再试").kv("userId", param.getUserId()).toString());
			throw PayExceptionCode.ERR0R_DUPLICATE.exp();
		}

		// 预先生成商户用户信息
		UserAuthPO userAuthPO = userAuthService.queryByUserIdAndMerchantCode(param.getUserId(), merchantCode);
		String userAuthId = "";
		if (null == userAuthPO) {
			userAuthId = sequenceService.sequenceStr(SequenceStrategy.PAY_USER);
			try {
				userAuthPO = buildUserAuth(DesensitizationUtil.convertIdCard(param.getIdNo()), CryptUtil.encrypt(param.getIdNo()), merchantCode,
						param.getAccountName(), param.getUserId(), userAuthId);
			} catch (Exception e) {
				logger.warn(Log.op(OP).msg("加密证件编号异常").kv("idNo", param.getIdNo()).kv("orderId", param.getOrderId()).toString(), e);
				throw PayExceptionCode.SYS_ERROR.exp();
			}
			userAuthService.insert(userAuthPO);
		}

		// 构建绑卡记录,并保存订单信息
		ProtocolBindingOrderDO po = buildProtocolBindOrder(param, merchantCode, userAuthPO.getUserAuthId());
		try {
			saveOrder(po);
		} catch (DuplicateKeyException e) {
			ProtocolBindingOrderDO existPO = protocolBindingOrderDao.selectByMerchantCodeAndOrderId(po.getMerchantCode(), po.getOrderId());
			if (null == existPO) {
				logger.error(Log.op(OP).msg("富友协议绑卡订单主键冲突").kv("orderId", po.getOrderId()).kv("busi", po.getBusiId()).toString());
				throw PayExceptionCode.SYS_ERROR.exp();
			}
			// 第三方请求验证码不支持重复订单
			logger.warn(Log.op(OP).msg("富友协议支付绑卡订单重复").kv("orderId", param.getOrderId()).toString());
			throw PayExceptionCode.ERROR_ORDER_DUPLICATE.exp();
		}

		//请求第三方协议发短信接口
		ProtocolService adaptiveTradeService = protocolAdaptService.getAdaptiveTradeService(ProtocolEnum.form(param.getPaymentCode()));
		OpResponse response = adaptiveTradeService.sendMessage(po);
		
		//生成短信接口令牌
		String token = UUID.randomUUID().toString().replaceAll("-", "");
		boolean isSave = cacheService.set(FuiouProtocolPayConstant.MESSAGE_TOKEN + po.getOrderId(), token, FuiouProtocolPayConstant.CACHETIME);
		if (!isSave) {
			logger.error(Log.op(OP).msg("缓存请求验证码token失败").kv("po", po).kv("token", token).toString());
			throw PayExceptionCode.SYS_ERROR.exp();
		}

		// 构建响应实体
		ProtocolBindSendMsgRsp rsp = new ProtocolBindSendMsgRsp();
		rsp.setToken(token);
		response.setContent(rsp);
		return response;
	}

	/**
	 * 协议绑定
	 * 
	 * @author wangjwy
	 * @date 2018/6/7 9:54
	 * @since 2.2.0
	 * @param params, merchantCode
	 * @return void
	 * @throws Exception
	 */
	@Override
	public OpResponse protocolBind(ProtocolBindReq params, String merchantCode) {
		final String OP = "ProtocolBindServiceImpl.protocolBind";
		String uuid = (String) cacheService.get(FuiouProtocolPayConstant.MESSAGE_TOKEN + params.getOrderId());
		if (StringUtils.isBlank(uuid) || !StringUtils.equals(params.getToken(), uuid)) {
			logger.warn(Log.op(OP).msg("协议绑定令牌错误或者已失效").kv("params", params).toString());
			throw PayExceptionCode.ERROR_TOKEN_ERROR.exp();
		}
		cacheService.delete(FuiouProtocolPayConstant.MESSAGE_TOKEN + params.getOrderId());
		// 查询订单
		ProtocolBindingOrderDO po = protocolBindingOrderDao.selectByMerchantCodeAndOrderId(merchantCode, params.getOrderId());
		if (null == po) {
			// 订单不存在
			logger.warn(Log.op(OP).msg("查询协议绑卡订单不存在,请先获取验证码").kv("orderId", params.getOrderId()).toString());
			throw PayExceptionCode.ERROR_MSGCODE_FIRST.exp();
		}
		// 判断订单状态
		if (OrderStateEnum.SUCCESS.isValue(String.valueOf(po.getState()))) {
			logger.info(Log.op(OP).msg("富友协议订单已经出结果,直接返回").kv("busiId", po.getBusiId()).kv("orderId", po.getOrderId()).toString());
			return OpResponse.success();
		}
		if (OrderStateEnum.FAIL.isValue(String.valueOf(po.getState()))) {
			logger.warn(Log.op(OP).msg("富友协议绑卡订单失败,直接返回").kv("busiId", po.getBusiId()).kv("orderId", po.getOrderId()).toString());
			throw new PayException(po.getRespCode(), po.getRespDesc());
		}
		// 查询支付凭证信息(需要userId)
		UserPaymentPO query = userPaymentService.query(merchantCode, po.getUserId(), PaymentEnum.FUIOU_PROTOCOL_PAY.getValue());
		if (query != null && StringUtils.isNotBlank(query.getProtocolCode()) && YesNoEnum.YES.isValue(query.getState())) {
			// 查到存在协议号,直接返回
			logger.info(Log.op(OP).msg("查到用户存已存在绑定协议号,直接返回").kv("orderId", params.getOrderId()).toString());
			throw PayExceptionCode.ERROR_EXISI_PROTOCOL.exp();
		}

		int i = protocolBindingOrderDao.modifyState(po.getBusiId(), Integer.valueOf(OrderStateEnum.DEALING.getValue()),
				Integer.valueOf(OrderStateEnum.INI.getValue()), null, null, null);
		if (1 != i) {
			logger.warn(Log.op(OP).msg("修改绑卡订单状态未命中,不做处理").kv("ProtocolBindingOrderDO", po).toString());
			OpResponse response = new OpResponse();
			response.setCode(PayExceptionCode.PROCESSING.getCode());
			response.setDisplay(PayExceptionCode.PROCESSING.getDesc());
			return response;
		}
		ProtocolService adaptiveTradeService = protocolAdaptService.getAdaptiveTradeService(ProtocolEnum.form(po.getPaymentCode()));
		OpResponse response = adaptiveTradeService.bindProtocol(po, params.getMessageCode());
		return response;
	}

	/**
	 * 查询协议卡状态
	 * 
	 * @author wangjwy
	 * @date 2018/6/15 14:51
	 * @since 2.2.0
	 * @param params, merchantCode
	 * @return com.yyfax.pay.vo.order.ProtocolCardQueryRsp
	 * @throws Exception
	 */
	@Override
	public OpResponse queryProtocolCard(ProtocolCardQueryReq params, String merchantCode) {
		final String OP = "ProtocolBindServiceImpl.queryProtocolCard";

		UserAuthPO userAuthPO = userAuthService.queryByUserIdAndMerchantCode(params.getUserId(), merchantCode);
		if (null == userAuthPO) {
			logger.warn(Log.op(OP).msg("查询到用户支付认证证信息不存在,请先进行协议绑定").kv("param", params).toString());
			throw PayExceptionCode.ERROR_USER_PAYMENT_NOT_EXISTS.exp();
		}
		UserPaymentPO userPaymentPO = userPaymentService.query(merchantCode, params.getUserId(), params.getPaymentCode());
		try {
			if (null != userPaymentPO && StringUtils.isNotBlank(userPaymentPO.getProtocolCode())
					&& StringUtils.equals(userPaymentPO.getState(), YesNoEnum.YES.getValue())) {
				logger.info(Log.op(OP).msg("查询到用户协议卡信息存在,直接返回").kv("userPayment", userPaymentPO).toString());
				OpResponse response = new OpResponse();
				response.setContent(buildProtocolCardQueryRsp(CryptUtil.decrypt(userPaymentPO.getCardNoCip()), userPaymentPO.getProtocolCode()));
				return response;
			}
		} catch (Exception e) {
			logger.error(Log.op(OP).msg("解密银行卡密文异常").kv("cardNoCip", userPaymentPO.getCardNoCip()).toString(), e);
			throw PayExceptionCode.SYS_ERROR.exp();
		}
		ProtocolService adaptiveTradeService = protocolAdaptService.getAdaptiveTradeService(ProtocolEnum.form(params.getPaymentCode()));
		OpResponse response = adaptiveTradeService.queryProtocol(params, merchantCode, userAuthPO.getUserAuthId(), userPaymentPO);
		return response;
	}

	/**
	 * 构建协议查询返回实体
	 * 
	 * @author wangjwy
	 * @date 2018/6/15 14:50
	 * @since 2.2.0
	 * @param cardNo, protocolCode
	 * @return com.yyfax.pay.vo.order.ProtocolCardQueryRsp
	 * @throws Exception
	 */
	private ProtocolCardQueryRsp buildProtocolCardQueryRsp(String cardNo, String protocolCode) {
		ProtocolCardQueryRsp rsp = new ProtocolCardQueryRsp();
		rsp.setCardNo(cardNo);
		rsp.setProtocolCode(protocolCode);
		rsp.setState(OrderStateEnum.SUCCESS.getValue());
		rsp.setDesc(PayExceptionCode.SUCCESS.getDesc());
		return rsp;
	}

	// @Override
	// public boolean queryResult(String busiId) {
	// final String OP = "ProtocolBindServiceImpl.queryResult";
	// ProtocolBindingOrderDO po = protocolBindingOrderDao.selectByPrimaryKey(busiId);
	// if (null == po || OrderStateEnum.isResult(po.getState().toString())) {
	// logger.warn(Log.op(OP).msg("查询到任务订单不存在|已出结果,不执行操作").kv("busiId", busiId).toString());
	// return true;
	// }
	// ProtocolCardQueryReq param = new ProtocolCardQueryReq();
	// param.setUserId(po.getUserId());
	// param.setPaymentCode(PaymentEnum.FUIOU_PROTOCOL_PAY.getValue());
	// param.setAccountName(po.getAccountName());
	// param.setUserId(po.getUserId());
	// param.setBankCode(po.getBankCode());
	// ProtocolCardQueryRsp rsp = null;
	// try {
	// rsp = queryProtocolCard(param, po.getMerchantCode());
	// if (StringUtils.equals(rsp.getState(), OrderStateEnum.DEALING.getValue())) {
	// protocolBindingOrderDao.modifyState(busiId,Integer.valueOf(rsp.getState()),Integer.valueOf(OrderStateEnum.DEALING.getValue()),PayExceptionCode.DEALING.getCode(),rsp.getDesc(),rsp.getProtocolCode());
	// return false;
	// }
	// } catch (Exception e) {
	// logger.error(Log.op(OP).msg("查询协议卡状态异常,等待下一次查询").toString());
	// return false;
	// }
	// protocolBindingOrderDao.modifyState(busiId,Integer.valueOf(rsp.getState()),Integer.valueOf(OrderStateEnum.DEALING.getValue()),PayExceptionCode.SUCCESS.getCode(),rsp.getDesc(),rsp.getProtocolCode());
	// return true;
	// }

	/**
	 * 构建用户认证信息
	 * 
	 * @author wangjwy
	 * @date 2018/6/19 10:51
	 * @since 2.2.0
	 * @param desensIdNo, idNoCip, merchantCode, name, userId, userAuthId
	 * @return com.yyfax.pay.order.model.po.UserAuthPO
	 * @throws Exception
	 */
	private UserAuthPO buildUserAuth(String desensIdNo, String idNoCip, String merchantCode, String name, String userId, String userAuthId) {
		UserAuthPO userAuthPO = new UserAuthPO();
		userAuthPO.setState(YesNoEnum.YES.getValue());
		userAuthPO.setIdNo(desensIdNo);
		userAuthPO.setIdNoCip(idNoCip);
		userAuthPO.setIdType(CertifTypeEnum.YYFAX_IDCARD.getValue().toString());
		userAuthPO.setMerchantCode(merchantCode);
		userAuthPO.setName(name);
		userAuthPO.setUserId(userId);
		userAuthPO.setUserAuthId(userAuthId);
		return userAuthPO;
	}

	/**
	 * 持久化订单
	 * 
	 * @author wangjwy
	 * @date 2018/6/7 9:55
	 * @since 2.2.0
	 * @param protocolBindingOrderDO
	 * @return void
	 * @throws Exception
	 */
	private void saveOrder(ProtocolBindingOrderDO protocolBindingOrderDO) {
		final String OP = "ProtocolBindServiceImpl.saveOrder";
		if (null == protocolBindingOrderDO) {
			logger.error(Log.op(OP).msg("参数为空").toString());
			throw PayExceptionCode.ERROR_ILL_PARAMETER.exp();
		}
		protocolBindingOrderDao.insert(protocolBindingOrderDO);

	}

	/**
	 * 构建实体
	 * 
	 * @author wangjwy
	 * @date 2018/6/7 9:56
	 * @since 2.2.0
	 * @param param, merchantCode
	 * @return com.yyfax.pay.order.model.domain.ProtocolBindingOrderDO
	 * @throws Exception
	 */
	public ProtocolBindingOrderDO buildProtocolBindOrder(ProtocolBindSendMsgReq param, String merchantCode, String userAuthId) {
		final String OP = "ProtocolBindServiceImpl.buildProtocolBindOrder";
		ProtocolBindingOrderDO po = new ProtocolBindingOrderDO();
		po.setBusiId(fuiouIdUtils.generateBusiId(FuiouBusiTypeEnum.PROTOCOL_BIND_CARD, merchantCode));
		po.setAccountName(param.getAccountName());
		po.setOrderId(param.getOrderId());
		po.setBankCode(param.getBankCode());
		po.setCertifTp(param.getCertifyType());
		po.setDataSrc(param.getDataSrc());
		po.setMerchantCode(merchantCode);
		po.setOrderTime(param.getOrderTime());
		po.setPhone(param.getPhone());
		po.setState(Integer.valueOf(OrderStateEnum.INI.getValue()));
		po.setUserId(param.getUserId());
		po.setUserAuthId(userAuthId);
		po.setPaymentCode(param.getPaymentCode());
		try {
			po.setCardNoCip(CryptUtil.encrypt(param.getCardNo()));
			po.setIdNoCip(CryptUtil.encrypt(param.getIdNo()));
		} catch (Exception e) {
			logger.error(Log.op(OP).msg("加密银行卡或身份证失败").toString(), e);
			throw PayExceptionCode.SYS_ERROR.exp();
		}
		return po;
	}
}
